package com.xx.robot.common.util.mongo;

import cn.hutool.json.JSONObject;
import com.mongodb.client.ListIndexesIterable;
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.Indexes;
import com.mongodb.client.result.UpdateResult;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;

/**
 * 获取mongoDB缓存数据
 */
@Component
public class MongoDbUtil {

    public static MongoDbUtil mongoDbUtil;

    @Autowired
    private MongoTemplate mongoTemplate;

    @PostConstruct
    public void init() {
        mongoDbUtil = this;
    }

    /**
     * 功能描述: 创建索引
     * 索引是顺序排列，且唯一的索引
     *
     * @param collectionName 集合名称，相当于关系型数据库中的表名
     * @param filedName      对象中的某个属性名
     * @return
     */
    public static String createIndex(String collectionName, String filedName) {
        //配置索引选项
        IndexOptions options = new IndexOptions();
        // 设置为唯一
        options.unique(true);
        //创建按filedName升序排的索引
        return mongoDbUtil.mongoTemplate.getCollection(collectionName).createIndex(Indexes.ascending(filedName), options);
    }

    /**
     * 功能描述: 获取当前集合对应的所有索引的名称
     *
     * @param collectionName 集合
     * @return java.util.List<java.lang.String>
     */
    public static List<String> getAllIndexes(String collectionName) {
        ListIndexesIterable<Document> list = mongoDbUtil.mongoTemplate.getCollection(collectionName).listIndexes();
        //上面的list不能直接获取size，因此初始化arrayList就不设置初始化大小了
        List<String> indexes = new ArrayList<>();
        for (Document document : list) {
            document.forEach((key1, value) -> {
                //提取出索引的名称
                if (key1.equals("name")) {
                    indexes.add(value.toString());
                }
            });
        }
        return indexes;
    }

    /**
     * 功能描述: 往对应的集合中插入一条数据
     *
     * @param info           存储对象
     * @param collectionName 集合名称
     * @return:void
     */
    public static <T> boolean insert(T info, String collectionName) {
        mongoDbUtil.mongoTemplate.insert(info, collectionName);
        return true;
    }

    /**
     * 功能描述: 往对应的集合中批量插入数据，注意批量的数据中不要包含重复的id
     *
     * @param infos 对象列表
     * @return:void
     */

    public static <T> boolean insertMulti(List<T> infos, String collectionName) {
        Collection<T> insert = mongoDbUtil.mongoTemplate.insert(infos, collectionName);
        return insert.size() == infos.size();
    }

    /**
     * 指定集合保存数据对象
     *
     * @param saveObj        数据对象
     * @param collectionName 集合名
     */
    public static <T> void save(T saveObj, String collectionName) {
        mongoDbUtil.mongoTemplate.save(saveObj, collectionName);
    }

    /**
     * 指定集合 修改数据，且仅修改找到的第一条数据
     *
     * @param accordingKey   修改条件 key
     * @param accordingValue 修改条件 value
     * @param updateKeys     修改内容 key数组
     * @param updateValues   修改内容 value数组
     * @param collectionName 集合名
     */
    public static long updateFirst(String accordingKey, Object accordingValue,
                                   String[] updateKeys, Object[] updateValues, String collectionName) {
        Criteria criteria = Criteria.where(accordingKey).is(accordingValue);
        Query query = Query.query(criteria);
        Update update = new Update();
        for (int i = 0; i < updateKeys.length; i++) {
            update.set(updateKeys[i], updateValues[i]);
        }
        UpdateResult updateResult = mongoDbUtil.mongoTemplate.updateFirst(query, update, collectionName);
        return updateResult.getModifiedCount();
    }

    public static long updateFirst(Query query,Update update,String collectionName) {
        UpdateResult updateResult = mongoDbUtil.mongoTemplate.updateFirst(query, update, collectionName);
        return updateResult.getModifiedCount();
    }


    public static <T> boolean update(Query query, Update update, Class<T> entityClass, String collectionName) {
        return mongoDbUtil.mongoTemplate.updateMulti(query, update, entityClass, collectionName).wasAcknowledged();
    }

    /**
     * 指定集合修改数据，且修改所找到的所有数据
     *
     * @param accordingKey   修改条件 key
     * @param accordingValue 修改条件 value
     * @param updateKeys     修改内容 key数组
     * @param updateValues   修改内容 value数组
     * @param collectionName 集合名
     */
    public static long updateMulti(String accordingKey, Object accordingValue,
                                   String[] updateKeys, Object[] updateValues, String collectionName) {
        Criteria criteria = Criteria.where(accordingKey).is(accordingValue);
        Query query = Query.query(criteria);
        Update update = new Update();
        for (int i = 0; i < updateKeys.length; i++) {
            update.set(updateKeys[i], updateValues[i]);
        }
        UpdateResult updateResult = mongoDbUtil.mongoTemplate.updateMulti(query, update, collectionName);
        return updateResult.getModifiedCount();
    }

    /**
     * 功能描述: 根据id删除集合中的内容
     *
     * @param value          序列id
     * @param collectionName 集合名称
     * @param clazz          集合中对象的类型
     * @return:void
     */

    public static <T> boolean deleteById(String value, Class<T> clazz, String collectionName) {
        // 设置查询条件，当id=#{id}
        Query query = new Query(Criteria.where("_id").is(value));
        return deleteByQuery(query, clazz, collectionName);
    }

    /**
     * 根据key，value到指定集合删除数据
     *
     * @param key            键
     * @param value          值
     * @param collectionName 集合名
     */
    public static <T> boolean deleteByKey(String key, Object value, Class<T> clazz, String collectionName) {
        Query query = new Query();
        query.addCriteria(new Criteria(key).is(value));
        return deleteByQuery(query, clazz, collectionName);
    }


    public static <T> boolean deleteByQuery(Query query, Class<T> clazz, String collectName) {
        return mongoDbUtil.mongoTemplate.remove(query, clazz, collectName).wasAcknowledged();
    }

    //-------------------------------------查询------------------------------


    /**
     * 功能描述: 根据id查询信息
     *
     * @param id             注解
     * @param clazz          类型
     * @param collectionName 集合名称
     * @return : T
     */
    public static <T> T selectById(String id, Class<T> clazz, String collectionName) {
        // 查询对象的时候，不仅需要传入id这个唯一键，还需要传入对象的类型，以及集合的名称
        return mongoDbUtil.mongoTemplate.findById(id, clazz, collectionName);
    }

    public static <T> T selectOneByQuery(Query query, Class<T> clazz, String collectName) {
        return mongoDbUtil.mongoTemplate.findOne(query, clazz, collectName);
    }

    /**
     * 指定集合 根据条件查询出符合的第一条数据
     *
     * @param entity         数据对象
     * @param findKeyArr     查询条件 key
     * @param findValueArr   查询条件 value
     * @param collectionName 集合名
     * @return 符合的第一条数据
     */
    public static <T> T selectOneByQuery(String[] findKeyArr, Object[] findValueArr, Class<T> entity, String collectionName) {
        Criteria criteria = new Criteria();
        for (int i = 0; i < findKeyArr.length; i++) {
            if (i == 0) {
                criteria = Criteria.where(findKeyArr[i]).is(findValueArr[i]);
            } else {
                criteria.and(findKeyArr[i]).is(findValueArr[i]);
            }
        }
        Query query = Query.query(criteria);
        return mongoDbUtil.mongoTemplate.findOne(query, entity, collectionName);
    }


    /**
     * 功能描述: 根据条件查询集合
     *
     * @param collectName 集合名称
     * @param query       查询条件
     * @param clazz       对象类型
     * @return
     */
    public static <T> List<T> selectByQuery(Query query, Class<T> clazz, String collectName) {
        return mongoDbUtil.mongoTemplate.find(query, clazz, collectName);
    }

    /**
     * 指定集合 根据条件查询出所有结果集
     *
     * @param entity         数据对象
     * @param findKeyArr     查询条件 key
     * @param findValueArr   查询条件 value
     * @param collectionName 集合名
     * @return
     */
    public static <T> List<T> selectByQuery(String[] findKeyArr, Object[] findValueArr, Class<T> entity, String collectionName) {
        Criteria criteria = new Criteria();
        for (int i = 0; i < findKeyArr.length; i++) {
            if (i == 0) {
                criteria = Criteria.where(findKeyArr[i]).is(findValueArr[i]);
            } else {
                criteria.and(findKeyArr[i]).is(findValueArr[i]);
            }
        }
        Query query = Query.query(criteria);
        return mongoDbUtil.mongoTemplate.find(query, entity, collectionName);
    }

    /**
     * 指定集合 根据条件查询出所有结果集 并排倒序
     *
     * @param findKeyArr     查询条件 key
     * @param findValueArr   查询条件 value
     * @param collectionName 集合名
     * @param sort           排序字段
     * @return
     */
    public static List<JSONObject> selectByQueryDesc(String[] findKeyArr, Object[] findValueArr, String sort, String collectionName) {
        Criteria criteria = new Criteria();
        for (int i = 0; i < findKeyArr.length; i++) {
            if (i == 0) {
                criteria = Criteria.where(findKeyArr[i]).is(findValueArr[i]);
            } else {
                criteria.and(findKeyArr[i]).is(findValueArr[i]);
            }
        }
        Query query = Query.query(criteria);
        query.with(Sort.by(Sort.Direction.DESC, sort));
        return mongoDbUtil.mongoTemplate.find(query, JSONObject.class, collectionName);
    }


    public static <T> List<T> aggregateData(Aggregation aggregation, String collectionName, Class<T> clazz) {
        return aggregate(aggregation, collectionName, clazz).getMappedResults();
    }

    public static <T> AggregationResults<T> aggregate(Aggregation aggregation, String collectionName, Class<T> clazz) {
        return mongoDbUtil.mongoTemplate.aggregate(aggregation, collectionName, clazz);
    }


    /**
     * 指定集合 根据条件查询出所有结果集 并排倒序
     *
     * @param findKeyArr     查询条件 key
     * @param findValueArr   查询条件 value
     * @param collectionName 集合名
     * @param sort           排序字段
     * @return
     */
    public static List<JSONObject> selectByAggregation(String collectionName, String[] findKeyArr, Object[] findValueArr, String sort, long limitNum) {
        Criteria criteria = new Criteria();
        for (int i = 0; i < findKeyArr.length; i++) {
            if (i == 0) {
                criteria = Criteria.where(findKeyArr[i]).is(findValueArr[i]);
            } else {
                criteria.and(findKeyArr[i]).is(findValueArr[i]);
            }
        }

        MatchOperation match = Aggregation.match(criteria);
        SortOperation sortBy = Aggregation.sort(Sort.Direction.DESC, sort);
        LimitOperation limit = Aggregation.limit(limitNum);
        Aggregation aggregation = Aggregation.newAggregation(match, sortBy, limit);
        AggregationResults<JSONObject> groupXbb = mongoDbUtil.mongoTemplate.aggregate(aggregation, collectionName, JSONObject.class);
        return groupXbb.getMappedResults();

    }

    /**
     * 功能描述: 查询所有
     *
     * @param collectName 集合名称
     * @param clazz       对象类型
     * @return
     */
    public static <T> List<T> selectAll(Class<T> clazz, String collectName) {
        return mongoDbUtil.mongoTemplate.findAll(clazz, collectName);
    }


    public static long countByQuery(String collectName, Query query) {
        return mongoDbUtil.mongoTemplate.count(query, collectName);
    }


    /**
     * @param collectName 集合名字 只做了总数是否有的判断
     * @param page        page 里面包含分页的相关信息，数据查询完成后，会赋值到page的data属性中
     * @param query       条件  里面不要包含分页条件
     * @param clazz       接受查询数据的类型class
     * @param <T>
     * @return
     */
    public static <T> MongoPage<T> pageByQuery(String collectName, MongoPage<T> page, Query query, Class<T> clazz) {
        long count = countByQuery(collectName, query);
        page.setTotal(count);
        query.skip(page.getSkip()).limit(page.getSize());
        List<T> data = selectByQuery(query, clazz, collectName);
        page.setData(data);
        return page;
    }

    /**
     * aggregate分页
     *
     * @param conditions 已经写好的条件 不要添加count和skip limit
     * @param page       分页结果,请事先填充正确的MongoDBPage#current,MongoDBPage#size
     * @param clazz      接受结果的类型
     * @param <T>
     * @return
     */
    public <T> MongoPage<T> aggregatePage(List<AggregationOperation> conditions, String collectionName, MongoPage<T> page, Class<T> clazz) {
        return aggregatePage(conditions, null, collectionName, page, clazz);
    }

    public <T> MongoPage<T> aggregatePage(List<AggregationOperation> conditions, Sort sort, String collectionName, MongoPage<T> page, Class<T> clazz) {
        conditions.add(Aggregation.count().as("count"));
        List<HashMap> hashMaps = aggregateData(Aggregation.newAggregation(conditions), collectionName, HashMap.class);
        long total = hashMaps.isEmpty() ? 0L : Long.parseLong(String.valueOf(hashMaps.get(0).get("count")));
        page.setTotal(total);
        conditions.remove(conditions.size() - 1);
        if (sort != null) {
            conditions.add(Aggregation.sort(sort));
        }
        conditions.add(Aggregation.skip(page.getSkip()));
        conditions.add(Aggregation.limit(page.getSize()));
        Aggregation aggregation = Aggregation.newAggregation(conditions);
        List<T> ts = aggregateData(aggregation, collectionName, clazz);
        page.setData(ts);
        return page;
    }


}
